library(reticulate)
use_python("/home/nealpsmith/.conda/envs/sc_analysis/bin/python")

First, we need to aggregate the matrices and assess the data quality. Here we use Pegasus to create a count matrix and to calcluate some quality-control statistics (% mitochondrial UMIs, number of genes per cell). From there, we can plot these statistics to determine the proper cutoffs to define the quality cells. Based on the distributions, we chose to include cells with < 30% mitochondrial reads and > 500 genes

import pegasus as pg
import scanpy as sc
import pandas as pd
import matplotlib.colors as clr
import matplotlib.pyplot as plt
import matplotlib as mpl

# Set a colormap
colormap = clr.LinearSegmentedColormap.from_list('gene_cmap', ["#d3d3d3" ,'#482cc7'], N=200)

# Aggregate the matrices
pg.aggregate_matrices(csv_file = "/home/nealpsmith/projects/medoff/cellranger/aggregate_matrix.csv",
                      what_to_return = "/home/nealpsmith/projects/medoff/data/all_data.h5sc")
2021-09-30 19:25:41,204 - pegasus - INFO - Time spent on 'read_input' = 0.21s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500008_ANA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:41,392 - pegasus - INFO - Time spent on 'read_input' = 0.19s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500008_ANA_Dil_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:41,591 - pegasus - INFO - Time spent on 'read_input' = 0.20s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500008_ANA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:41,931 - pegasus - INFO - Time spent on 'read_input' = 0.34s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500012_ANA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:42,305 - pegasus - INFO - Time spent on 'read_input' = 0.37s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500012_ANA_Dil_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:42,721 - pegasus - INFO - Time spent on 'read_input' = 0.41s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500012_ANA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:43,229 - pegasus - INFO - Time spent on 'read_input' = 0.51s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500021_AA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:43,770 - pegasus - INFO - Time spent on 'read_input' = 0.54s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500021_AA_Dil_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:44,033 - pegasus - INFO - Time spent on 'read_input' = 0.26s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500021_AA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:44,305 - pegasus - INFO - Time spent on 'read_input' = 0.27s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500024_ANA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:44,617 - pegasus - INFO - Time spent on 'read_input' = 0.31s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500024_ANA_Dil_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:45,027 - pegasus - INFO - Time spent on 'read_input' = 0.41s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500024_ANA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:45,180 - pegasus - INFO - Time spent on 'read_input' = 0.15s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500015_ANA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:45,441 - pegasus - INFO - Time spent on 'read_input' = 0.26s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500015_ANA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:45,992 - pegasus - INFO - Time spent on 'read_input' = 0.55s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500030_AA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:46,260 - pegasus - INFO - Time spent on 'read_input' = 0.27s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500030_AA_Dil_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:46,447 - pegasus - INFO - Time spent on 'read_input' = 0.18s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500030_AA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:46,823 - pegasus - INFO - Time spent on 'read_input' = 0.37s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500032_AA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:47,260 - pegasus - INFO - Time spent on 'read_input' = 0.43s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500032_AA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:47,677 - pegasus - INFO - Time spent on 'read_input' = 0.41s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500035_AA_Pre_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:47,934 - pegasus - INFO - Time spent on 'read_input' = 0.25s.
Processed /home/nealpsmith/projects/medoff/data/h5_files/500035_AA_Ag_filtered_feature_bc_matrix.h5.
2021-09-30 19:25:49,101 - pegasus - INFO - Time spent on 'aggregate' = 1.16s.
2021-09-30 19:25:56,707 - pegasus - INFO - Time spent on 'write_output' = 7.61s.
Aggregated 21 files.
all_data = pg.read_input("/home/nealpsmith/projects/medoff/data/all_data.h5sc")
2021-09-30 19:25:59,680 - pegasus - INFO - Time spent on 'read_input' = 2.95s.
pg.qc_metrics(all_data, percent_mito = 30)

# Plot the percent mito/n genes
fig, ax = plt.subplots(1)
x = all_data.obs["n_genes"]
y = all_data.obs["percent_mito"]
_ = ax.hexbin(x, y, mincnt=1, xscale = "log")
_ = ax.set_xticks([10, 100, 1000])
_ = ax.get_xaxis().set_major_formatter(mpl.ticker.ScalarFormatter())
_ = ax.axvline(500, color="red")
_ = ax.axhline(30, color="red")
_ = plt.xlabel("Number of genes")
_ = plt.ylabel("percent mitochondrial UMIs")

Next we log-normalized the data, selected highly variable features and performed PCA

pg.filter_data(all_data)
pg.log_norm(all_data)
pg.highly_variable_features(all_data, consider_batch = False)
pg.pca(all_data)

To account for technical variability between samples, we used the Harmony algorithm to align the pricniple component scores. These adjust PCs were used for downstrseam leiden clustering and UMAP dimensionality reduction. Once we have the data in UMAP space, we can look at our QC metrics, along with our basic leiden clustering.

# pg.run_harmony(all_data, n_jobs = 5)
# pg.neighbors(all_data, rep = "pca_harmony")
# pg.diffmap(all_data, rep = "pca_harmony")
# pg.leiden(all_data, rep = "pca_harmony")
# pg.umap(all_data, rep = "pca_harmony")
# pg.write_output("/home/nealpsmith/projects/medoff/data/all_data_harmonized.h5ad")

# Read in the harmonized data
harmonized_data = pg.read_input("/home/nealpsmith/projects/medoff/data/all_data_harmonized.h5ad")
2021-09-30 19:26:22,066 - pegasus - INFO - Time spent on 'read_input' = 5.39s.
figure = sc.pl.umap(harmonized_data, color = ["n_genes", "percent_mito", "leiden_labels"],
                    cmap = colormap, return_fig = True, show = False, ncols = 2)
figure.set_size_inches(11, 11)
figure

Now that the data is in UMAP space, we can use cannonical markers to try to define major lineages.

lin_genes = ["EPCAM", "CD8A", "IL7R", "LYZ", "MS4A1", "CPA3", "GNLY"]
figure = sc.pl.umap(harmonized_data, color = lin_genes,
           cmap = colormap, ncols = 3, return_fig = True, show = False,
                    wspace = 0.2, hspace = 0.3)
figure.set_size_inches(10, 7)

Using all of this info (along with other DEG info), we can assign the clusters to our major lineages.

cell_clust_dict = {
        "1" : "CD8 T cells",
        "2" : "CD4 T cells",
        "3" : "Epithelial",
        "4" : "MPS",
        "5" : "Epithelial",
        "6" : "Epithelial",
        "7" : "Epithelial",
        "8" : "CD8 T and NK cells",
        "9" : "Epithelial",
        "10" : "MPS",
        "11" : "B cells",
        "12" : "CD8 T cells",
        "13" : "MPS",
        "14" : "MPS",
        "15" : "CD8 T cells",
        "16" : "Mast cells",
        "17" : "Epithelial",
        "18" : "Epithelial",
        "19" : "Epithelial",
        "20" : "Epithelial"
    }
harmonized_data.obs["cell_set"] = [cell_clust_dict[clust] for clust in harmonized_data.obs["leiden_labels"]]

figure = sc.pl.umap(harmonized_data, color = "cell_set", return_fig = True, show = False, legend_loc = "on data")
... storing 'cell_set' as categorical
figure.set_size_inches(5, 5)
figure

Looking at the major lineage markers in a dot plot, we can appreciate how specific they are for our new assignments.

harmonized_data.obs["cell_set"] = pd.Categorical(harmonized_data.obs["cell_set"],
                                                 categories = ["Epithelial", "CD8 T cells", "CD4 T cells",
                                                               "MPS", "B cells", "Mast cells", "CD8 T and NK cells"])

plot = sc.pl.dotplot(harmonized_data, lin_genes, groupby="cell_set",
                     show=False, return_fig=True, title="lineage markers",
                     cmap=colormap, standard_scale = "var")
/home/nealpsmith/.conda/envs/sc_analysis/lib/python3.7/site-packages/pandas/core/arrays/categorical.py:2487: FutureWarning: The `inplace` parameter in pandas.Categorical.remove_unused_categories is deprecated and will be removed in a future version.
  res = method(*args, **kwargs)
axes_dict = plot.get_axes()
axes_dict["mainplot_ax"].set_axisbelow(True)
axes_dict["mainplot_ax"].grid()
_ = axes_dict["color_legend_ax"].set_title("scaled expression")
figure = plt.gcf()
figure.set_size_inches(8, 6)
figure

Looking at the embedding density across UMAP space, we can see there are biases in what types of cells were recovered from different sample types.

harmonized_data.obs["pheno_tmpt"] = ["_".join([pheno, tmpt]) for pheno, tmpt in zip(harmonized_data.obs["phenotype"], harmonized_data.obs["sample"])]
sc.tl.embedding_density(harmonized_data, basis = "umap", groupby = "pheno_tmpt", key_added = "pheno_tmpt_dens")
... storing 'pheno_tmpt' as categorical
figure = sc.pl.embedding_density(harmonized_data, basis = "umap", key = "pheno_tmpt_dens",
                                 return_fig = True, show = False,
                                 wspace = 0.2, hspace = 0.4, ncols = 3)
figure.set_size_inches(10, 7)

LS0tCnRpdGxlOiAiRmlndXJlIDEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocmV0aWN1bGF0ZSkKdXNlX3B5dGhvbigiL2hvbWUvbmVhbHBzbWl0aC8uY29uZGEvZW52cy9zY19hbmFseXNpcy9iaW4vcHl0aG9uIikKYGBgCgpGaXJzdCwgd2UgbmVlZCB0byBhZ2dyZWdhdGUgdGhlIG1hdHJpY2VzIGFuZCBhc3Nlc3MgdGhlIGRhdGEgcXVhbGl0eS4gIEhlcmUgd2UgdXNlIFBlZ2FzdXMgdG8gY3JlYXRlIGEgY291bnQgbWF0cml4IGFuZCB0bwpjYWxjbHVhdGUgc29tZSBxdWFsaXR5LWNvbnRyb2wgc3RhdGlzdGljcyAoJSBtaXRvY2hvbmRyaWFsIFVNSXMsIG51bWJlciBvZiBnZW5lcyBwZXIgY2VsbCkuICBGcm9tIHRoZXJlLCB3ZSBjYW4gcGxvdAp0aGVzZSBzdGF0aXN0aWNzIHRvIGRldGVybWluZSB0aGUgcHJvcGVyIGN1dG9mZnMgdG8gZGVmaW5lIHRoZSBxdWFsaXR5IGNlbGxzLiAgQmFzZWQgb24gdGhlIGRpc3RyaWJ1dGlvbnMsIHdlIGNob3NlIHRvIGluY2x1ZGUKY2VsbHMgd2l0aCA8IDMwJSBtaXRvY2hvbmRyaWFsIHJlYWRzIGFuZCA+IDUwMCBnZW5lcwoKYGBge3B5dGhvbiBtZXNzYWdlPUZBTFNFfQppbXBvcnQgcGVnYXN1cyBhcyBwZwppbXBvcnQgc2NhbnB5IGFzIHNjCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IG1hdHBsb3RsaWIuY29sb3JzIGFzIGNscgppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0CmltcG9ydCBtYXRwbG90bGliIGFzIG1wbAoKIyBTZXQgYSBjb2xvcm1hcApjb2xvcm1hcCA9IGNsci5MaW5lYXJTZWdtZW50ZWRDb2xvcm1hcC5mcm9tX2xpc3QoJ2dlbmVfY21hcCcsIFsiI2QzZDNkMyIgLCcjNDgyY2M3J10sIE49MjAwKQoKIyBBZ2dyZWdhdGUgdGhlIG1hdHJpY2VzCnBnLmFnZ3JlZ2F0ZV9tYXRyaWNlcyhjc3ZfZmlsZSA9ICIvaG9tZS9uZWFscHNtaXRoL3Byb2plY3RzL21lZG9mZi9jZWxscmFuZ2VyL2FnZ3JlZ2F0ZV9tYXRyaXguY3N2IiwKICAgICAgICAgICAgICAgICAgICAgIHdoYXRfdG9fcmV0dXJuID0gIi9ob21lL25lYWxwc21pdGgvcHJvamVjdHMvbWVkb2ZmL2RhdGEvYWxsX2RhdGEuaDVzYyIpCgphbGxfZGF0YSA9IHBnLnJlYWRfaW5wdXQoIi9ob21lL25lYWxwc21pdGgvcHJvamVjdHMvbWVkb2ZmL2RhdGEvYWxsX2RhdGEuaDVzYyIpCnBnLnFjX21ldHJpY3MoYWxsX2RhdGEsIHBlcmNlbnRfbWl0byA9IDMwKQoKIyBQbG90IHRoZSBwZXJjZW50IG1pdG8vbiBnZW5lcwpmaWcsIGF4ID0gcGx0LnN1YnBsb3RzKDEpCnggPSBhbGxfZGF0YS5vYnNbIm5fZ2VuZXMiXQp5ID0gYWxsX2RhdGEub2JzWyJwZXJjZW50X21pdG8iXQpfID0gYXguaGV4YmluKHgsIHksIG1pbmNudD0xLCB4c2NhbGUgPSAibG9nIikKXyA9IGF4LnNldF94dGlja3MoWzEwLCAxMDAsIDEwMDBdKQpfID0gYXguZ2V0X3hheGlzKCkuc2V0X21ham9yX2Zvcm1hdHRlcihtcGwudGlja2VyLlNjYWxhckZvcm1hdHRlcigpKQpfID0gYXguYXh2bGluZSg1MDAsIGNvbG9yPSJyZWQiKQpfID0gYXguYXhobGluZSgzMCwgY29sb3I9InJlZCIpCl8gPSBwbHQueGxhYmVsKCJOdW1iZXIgb2YgZ2VuZXMiKQpfID0gcGx0LnlsYWJlbCgicGVyY2VudCBtaXRvY2hvbmRyaWFsIFVNSXMiKQoKYGBgCgpOZXh0IHdlIGxvZy1ub3JtYWxpemVkIHRoZSBkYXRhLCBzZWxlY3RlZCBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgYW5kIHBlcmZvcm1lZCBQQ0EKYGBge3B5dGhvbiBwcm9jZXNzX2RhdGEsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHMgPSBGQUxTRX0KcGcuZmlsdGVyX2RhdGEoYWxsX2RhdGEpCnBnLmxvZ19ub3JtKGFsbF9kYXRhKQpwZy5oaWdobHlfdmFyaWFibGVfZmVhdHVyZXMoYWxsX2RhdGEsIGNvbnNpZGVyX2JhdGNoID0gRmFsc2UpCnBnLnBjYShhbGxfZGF0YSkKYGBgCgpUbyBhY2NvdW50IGZvciB0ZWNobmljYWwgdmFyaWFiaWxpdHkgYmV0d2VlbiBzYW1wbGVzLCB3ZSB1c2VkIHRoZSBIYXJtb255IGFsZ29yaXRobSB0byBhbGlnbiB0aGUgcHJpY25pcGxlIGNvbXBvbmVudCBzY29yZXMuClRoZXNlIGFkanVzdCBQQ3Mgd2VyZSB1c2VkIGZvciBkb3duc3Ryc2VhbSBsZWlkZW4gY2x1c3RlcmluZyBhbmQgVU1BUCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24uICBPbmNlIHdlIGhhdmUgdGhlIGRhdGEgaW4gVU1BUCBzcGFjZSwKd2UgY2FuIGxvb2sgYXQgb3VyIFFDIG1ldHJpY3MsIGFsb25nIHdpdGggb3VyIGJhc2ljIGxlaWRlbiBjbHVzdGVyaW5nLgpgYGB7cHl0aG9uIGhhcm1vbml6ZX0KIyBwZy5ydW5faGFybW9ueShhbGxfZGF0YSwgbl9qb2JzID0gNSkKIyBwZy5uZWlnaGJvcnMoYWxsX2RhdGEsIHJlcCA9ICJwY2FfaGFybW9ueSIpCiMgcGcuZGlmZm1hcChhbGxfZGF0YSwgcmVwID0gInBjYV9oYXJtb255IikKIyBwZy5sZWlkZW4oYWxsX2RhdGEsIHJlcCA9ICJwY2FfaGFybW9ueSIpCiMgcGcudW1hcChhbGxfZGF0YSwgcmVwID0gInBjYV9oYXJtb255IikKIyBwZy53cml0ZV9vdXRwdXQoIi9ob21lL25lYWxwc21pdGgvcHJvamVjdHMvbWVkb2ZmL2RhdGEvYWxsX2RhdGFfaGFybW9uaXplZC5oNWFkIikKCiMgUmVhZCBpbiB0aGUgaGFybW9uaXplZCBkYXRhCmhhcm1vbml6ZWRfZGF0YSA9IHBnLnJlYWRfaW5wdXQoIi9ob21lL25lYWxwc21pdGgvcHJvamVjdHMvbWVkb2ZmL2RhdGEvYWxsX2RhdGFfaGFybW9uaXplZC5oNWFkIikKZmlndXJlID0gc2MucGwudW1hcChoYXJtb25pemVkX2RhdGEsIGNvbG9yID0gWyJuX2dlbmVzIiwgInBlcmNlbnRfbWl0byIsICJsZWlkZW5fbGFiZWxzIl0sCiAgICAgICAgICAgICAgICAgICAgY21hcCA9IGNvbG9ybWFwLCByZXR1cm5fZmlnID0gVHJ1ZSwgc2hvdyA9IEZhbHNlLCBuY29scyA9IDIpCmZpZ3VyZS5zZXRfc2l6ZV9pbmNoZXMoMTEsIDExKQpmaWd1cmUKYGBgCgoKTm93IHRoYXQgdGhlIGRhdGEgaXMgaW4gVU1BUCBzcGFjZSwgd2UgY2FuIHVzZSBjYW5ub25pY2FsIG1hcmtlcnMgdG8gdHJ5IHRvIGRlZmluZSBtYWpvciBsaW5lYWdlcy4KYGBge3B5dGhvbiBtYXJrZXJfZ2VuZXN9Cmxpbl9nZW5lcyA9IFsiRVBDQU0iLCAiQ0Q4QSIsICJJTDdSIiwgIkxZWiIsICJNUzRBMSIsICJDUEEzIiwgIkdOTFkiXQpmaWd1cmUgPSBzYy5wbC51bWFwKGhhcm1vbml6ZWRfZGF0YSwgY29sb3IgPSBsaW5fZ2VuZXMsCiAgICAgICAgICAgY21hcCA9IGNvbG9ybWFwLCBuY29scyA9IDMsIHJldHVybl9maWcgPSBUcnVlLCBzaG93ID0gRmFsc2UsCiAgICAgICAgICAgICAgICAgICAgd3NwYWNlID0gMC4yLCBoc3BhY2UgPSAwLjMpCmZpZ3VyZS5zZXRfc2l6ZV9pbmNoZXMoMTAsIDcpCgpgYGAKVXNpbmcgYWxsIG9mIHRoaXMgaW5mbyAoYWxvbmcgd2l0aCBvdGhlciBERUcgaW5mbyksIHdlIGNhbiBhc3NpZ24gdGhlIGNsdXN0ZXJzIHRvIG91ciBtYWpvciBsaW5lYWdlcy4KYGBge3B5dGhvbiBsaW5lYWdlX3VtYXB9CmNlbGxfY2x1c3RfZGljdCA9IHsKICAgICAgICAiMSIgOiAiQ0Q4IFQgY2VsbHMiLAogICAgICAgICIyIiA6ICJDRDQgVCBjZWxscyIsCiAgICAgICAgIjMiIDogIkVwaXRoZWxpYWwiLAogICAgICAgICI0IiA6ICJNUFMiLAogICAgICAgICI1IiA6ICJFcGl0aGVsaWFsIiwKICAgICAgICAiNiIgOiAiRXBpdGhlbGlhbCIsCiAgICAgICAgIjciIDogIkVwaXRoZWxpYWwiLAogICAgICAgICI4IiA6ICJDRDggVCBhbmQgTksgY2VsbHMiLAogICAgICAgICI5IiA6ICJFcGl0aGVsaWFsIiwKICAgICAgICAiMTAiIDogIk1QUyIsCiAgICAgICAgIjExIiA6ICJCIGNlbGxzIiwKICAgICAgICAiMTIiIDogIkNEOCBUIGNlbGxzIiwKICAgICAgICAiMTMiIDogIk1QUyIsCiAgICAgICAgIjE0IiA6ICJNUFMiLAogICAgICAgICIxNSIgOiAiQ0Q4IFQgY2VsbHMiLAogICAgICAgICIxNiIgOiAiTWFzdCBjZWxscyIsCiAgICAgICAgIjE3IiA6ICJFcGl0aGVsaWFsIiwKICAgICAgICAiMTgiIDogIkVwaXRoZWxpYWwiLAogICAgICAgICIxOSIgOiAiRXBpdGhlbGlhbCIsCiAgICAgICAgIjIwIiA6ICJFcGl0aGVsaWFsIgogICAgfQpoYXJtb25pemVkX2RhdGEub2JzWyJjZWxsX3NldCJdID0gW2NlbGxfY2x1c3RfZGljdFtjbHVzdF0gZm9yIGNsdXN0IGluIGhhcm1vbml6ZWRfZGF0YS5vYnNbImxlaWRlbl9sYWJlbHMiXV0KCmZpZ3VyZSA9IHNjLnBsLnVtYXAoaGFybW9uaXplZF9kYXRhLCBjb2xvciA9ICJjZWxsX3NldCIsIHJldHVybl9maWcgPSBUcnVlLCBzaG93ID0gRmFsc2UsIGxlZ2VuZF9sb2MgPSAib24gZGF0YSIpCmZpZ3VyZS5zZXRfc2l6ZV9pbmNoZXMoNSwgNSkKZmlndXJlCgpgYGAKCkxvb2tpbmcgYXQgdGhlIG1ham9yIGxpbmVhZ2UgbWFya2VycyBpbiBhIGRvdCBwbG90LCB3ZSBjYW4gYXBwcmVjaWF0ZSBob3cgc3BlY2lmaWMgdGhleSBhcmUgZm9yIG91ciBuZXcgYXNzaWdubWVudHMuCmBgYHtweXRob24gZG90cGxvdH0KaGFybW9uaXplZF9kYXRhLm9ic1siY2VsbF9zZXQiXSA9IHBkLkNhdGVnb3JpY2FsKGhhcm1vbml6ZWRfZGF0YS5vYnNbImNlbGxfc2V0Il0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWVzID0gWyJFcGl0aGVsaWFsIiwgIkNEOCBUIGNlbGxzIiwgIkNENCBUIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1QUyIsICJCIGNlbGxzIiwgIk1hc3QgY2VsbHMiLCAiQ0Q4IFQgYW5kIE5LIGNlbGxzIl0pCgpwbG90ID0gc2MucGwuZG90cGxvdChoYXJtb25pemVkX2RhdGEsIGxpbl9nZW5lcywgZ3JvdXBieT0iY2VsbF9zZXQiLAogICAgICAgICAgICAgICAgICAgICBzaG93PUZhbHNlLCByZXR1cm5fZmlnPVRydWUsIHRpdGxlPSJsaW5lYWdlIG1hcmtlcnMiLAogICAgICAgICAgICAgICAgICAgICBjbWFwPWNvbG9ybWFwLCBzdGFuZGFyZF9zY2FsZSA9ICJ2YXIiKQoKYXhlc19kaWN0ID0gcGxvdC5nZXRfYXhlcygpCmF4ZXNfZGljdFsibWFpbnBsb3RfYXgiXS5zZXRfYXhpc2JlbG93KFRydWUpCmF4ZXNfZGljdFsibWFpbnBsb3RfYXgiXS5ncmlkKCkKXyA9IGF4ZXNfZGljdFsiY29sb3JfbGVnZW5kX2F4Il0uc2V0X3RpdGxlKCJzY2FsZWQgZXhwcmVzc2lvbiIpCmZpZ3VyZSA9IHBsdC5nY2YoKQpmaWd1cmUuc2V0X3NpemVfaW5jaGVzKDgsIDYpCmZpZ3VyZQpgYGAKCkxvb2tpbmcgYXQgdGhlIGVtYmVkZGluZyBkZW5zaXR5IGFjcm9zcyBVTUFQIHNwYWNlLCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSBiaWFzZXMgaW4gd2hhdCB0eXBlcyBvZiBjZWxscyB3ZXJlIHJlY292ZXJlZCBmcm9tIGRpZmZlcmVudCBzYW1wbGUgdHlwZXMuCmBgYHtweXRob24gZW1iZWRkaW5nX2RlbnNpdHl9Cmhhcm1vbml6ZWRfZGF0YS5vYnNbInBoZW5vX3RtcHQiXSA9IFsiXyIuam9pbihbcGhlbm8sIHRtcHRdKSBmb3IgcGhlbm8sIHRtcHQgaW4gemlwKGhhcm1vbml6ZWRfZGF0YS5vYnNbInBoZW5vdHlwZSJdLCBoYXJtb25pemVkX2RhdGEub2JzWyJzYW1wbGUiXSldCnNjLnRsLmVtYmVkZGluZ19kZW5zaXR5KGhhcm1vbml6ZWRfZGF0YSwgYmFzaXMgPSAidW1hcCIsIGdyb3VwYnkgPSAicGhlbm9fdG1wdCIsIGtleV9hZGRlZCA9ICJwaGVub190bXB0X2RlbnMiKQpmaWd1cmUgPSBzYy5wbC5lbWJlZGRpbmdfZGVuc2l0eShoYXJtb25pemVkX2RhdGEsIGJhc2lzID0gInVtYXAiLCBrZXkgPSAicGhlbm9fdG1wdF9kZW5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuX2ZpZyA9IFRydWUsIHNob3cgPSBGYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd3NwYWNlID0gMC4yLCBoc3BhY2UgPSAwLjQsIG5jb2xzID0gMykKZmlndXJlLnNldF9zaXplX2luY2hlcygxMCwgNykKCmBgYA==